package com.wcj;

import com.wcj.annotation.Component;
import com.wcj.annotation.Qualifier;
import com.wcj.annotation.Value;
import com.wcj.tools.FileUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author wcj
 * @Date 2021/6/29 15:55
 * @Version 1.0
 */
public class MyAnnotationContextApplication {

    private static Map<String, Object> ioc = new HashMap<>();
    private static List<String> beanNames = new ArrayList<>();

    public MyAnnotationContextApplication(String packName) {
        // 扫描指定包，获取包中类的全类名
        scanPackage(packName);
        // 获取原材料，封装为BeanDefinition对象
        Set<BeanDefinition> beanDefinitions = findBeanDefinitions(beanNames);
        // 遍历beanDefinitions，创建对象，beanName-beanObj放入到IOC容器
        initIoc(beanDefinitions);
        // 自动装载
        autowired(beanDefinitions);
    }

    /**
     * 获取bean
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return ioc.get(beanName);
    }

    /**
     * 自动装载机制：@Autowired根据类型，@Qualifier
     *
     * @param beanDefinitions
     */
    private void autowired(Set<BeanDefinition> beanDefinitions) {
        if (beanDefinitions.size() < 1) {
            return;
        }
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()) {
            BeanDefinition beanDefinition = iterator.next();
            Class<?> clazz = beanDefinition.getClazz();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Qualifier qualifierAnnotation = declaredField.getAnnotation(Qualifier.class);
                if (qualifierAnnotation != null) {
                    // byName
                    String value = qualifierAnnotation.value();
                    try {
                        String fieldName = declaredField.getName();
                        Object bean = getBean(value);
                        Object obj = getBean(beanDefinition.getBeanName());
                        Method method = clazz.getMethod("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), bean.getClass());
                        method.invoke(obj, bean);
                    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 遍历BeanDefinitions，创建对象，beanName-beanObj放入到IOC容器中
     *
     * @param beanDefinitions
     */
    private void initIoc(Set<BeanDefinition> beanDefinitions) {
        if (beanDefinitions.size() < 1) {
            return;
        }
        for (BeanDefinition beanDefinition : beanDefinitions) {
            try {
                // 创建对象
                Class<?> clazz = beanDefinition.getClazz();
                Object obj = clazz.getConstructor(null).newInstance(null);
                // 注入属性
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    // @Value注解属性赋值
                    Value valueAnnotation = field.getAnnotation(Value.class);
                    if (valueAnnotation != null) {
                        String value = valueAnnotation.value();
                        String type = field.getAnnotatedType().getType().getTypeName().replace("java.lang.", "");
                        Method method = clazz.getMethod("set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1), field.getType());
                        Object val = null;
                        switch (type) {
                            case "String":
                                val = value;
                                break;
                            case "Integer":
                                val = Integer.valueOf(value);
                                break;
                            case "Float":
                                val = Float.valueOf(value);
                                break;
                            case "Long":
                                val = Long.valueOf(value);
                                break;
                            case "Byte":
                                val = Byte.valueOf(value);
                                break;
                            default:
                                throw new RuntimeException("no such type!");
                        }
                        method.invoke(obj, val);
                    }
                }
                ioc.put(beanDefinition.getBeanName(), obj);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取原材料：1、class对象；2、beanName(类名首字母小写)
     *
     * @param classNames
     */
    private Set<BeanDefinition> findBeanDefinitions(List<String> classNames) {
        if (classNames.size() < 1) {
            return null;
        }
        Set<BeanDefinition> beanDefinitions = new HashSet<>();
        for (String className : classNames) {
            try {
                // 通过全类名获取类的class对象
                Object o = Class.forName(className).getConstructor(null).newInstance(null);
                Class<?> clazz = o.getClass();
                // 获取beanName
                String name = className.substring(className.lastIndexOf(".") + 1);
                String beanName = name.substring(0, 1).toLowerCase() + name.substring(1);
                // 组装BeanDefinition对象
                BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
                beanDefinitions.add(beanDefinition);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return beanDefinitions;
    }

    /**
     * 扫描包，获取添加有@Component注解的类
     *
     * @param packName
     * @return
     */
    private void scanPackage(String packName) {
        List<String> classNames = FileUtils.scanPackage(packName);
        if (classNames.size() < 1) {
            return;
        }
        for (String className : classNames) {
            // 找出包含注解@Component的条目
            try {
                Class<?> clazz = Class.forName(className);
                Component componentAnnotation = clazz.getAnnotation(Component.class);
                if (componentAnnotation != null) {
                    beanNames.add(className);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}
